/*
 * Ellipse.java  0.4.0 / Oct 24, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing.graphics;

import java.util.NoSuchElementException;

/**
 * Ellipse Shape.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class Ellipse extends RectangularShape {

// Private Fields. -------------------------------------------------------------

  private final Rectangle bounds;
  private int hashCode; // NOPMD

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new Ellipse object.
   */
  public Ellipse() {
    bounds = new Rectangle();
  }

  /**
   * Constructs new Ellipse object.
   *
   * @param x the x coordinate of the ellipse
   * @param y the y coordinate of the ellipse
   * @param width the width of the ellipse
   * @param height the height of the ellipse
   */
  public Ellipse(int x, int y, int width, int height) {
    bounds = new Rectangle(x, y, width, height);
  }

// Shape Interface Implementation. ---------------------------------------------

  /**
   * {@inheritDoc}
   */
  public boolean contains(int x, int y) {
    double rx = getWidth() >> 1;
    double ry = getHeight() >> 1;
    double tx = (x - (getX() + rx)) / rx;
    double ty = (y - (getY() + ry)) / ry;
    return (((tx * tx) + (ty * ty)) < 1.0);
  }

  /**
   * {@inheritDoc}
   */
  public Rectangle getBounds() {
    return bounds;
  }

  /**
   * {@inheritDoc}
   */
  public PathIterator getPathIterator() {
    return new EllipsePathIterator(this);
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if ((obj != null) && (obj.getClass() == getClass())) {
      Ellipse e = (Ellipse)obj;
      return ((e.getX() == getX()) && (e.getY() == getY()) &&
              (e.getWidth() == getWidth()) && (e.getHeight() == getHeight()));
    }
    return false;
  }

  /**
   * {@inheritDoc}
   */
  public int hashCode() {
    if (hashCode == 0) {
      int hashCode = 47;
      hashCode = 487 * hashCode + getX();
      hashCode = 487 * hashCode + getY();
      hashCode = 487 * hashCode + getWidth();
      hashCode = 487 * hashCode + getHeight();
      this.hashCode = hashCode;
    }
    return hashCode;
  }

  /**
   * {@inheritDoc}
   */
  public String toString() {
    return getClass().getName() + "[x=" + getX() + ",y=" + getY() + ",width=" +
           getWidth() + ",height=" + getHeight() + "]";
  }

  /**
   * {@inheritDoc}
   */
  public int getX() {
    return bounds.getX();
  }

  /**
   * {@inheritDoc}
   */
  public int getY() {
    return bounds.getY();
  }

  /**
   * {@inheritDoc}
   */
  public int getWidth() {
    return bounds.getWidth();
  }

  /**
   * {@inheritDoc}
   */
  public int getHeight() {
    return bounds.getHeight();
  }

// Inner Classes. --------------------------------------------------------------

  /**
   * Ellipse Path Iterator.
   */
  protected static class EllipsePathIterator implements PathIterator {

    private final int x;
    private final int y;
    private final int width;
    private final int height;
    private int iteration;

    /**
     * Constructs new EllipsePathIterator object.
     *
     * @param ellipse Ellipse
     */
    public EllipsePathIterator(Ellipse ellipse) {
      x = ellipse.getX();
      y = ellipse.getY();
      width = ellipse.getWidth();
      height = ellipse.getHeight();
      if ((width < 0) || (height < 0)) {
        iteration = 6;
      }
    }

    /**
     * {@inheritDoc}
     */
    public int getWindingRule() {
      return WIND_NON_ZERO;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isDone() {
      return (iteration > 5);
    }

    /**
     * {@inheritDoc}
     */
    public void next() {
      iteration++;
    }

    /**
     * {@inheritDoc}
     */
    public int currentSegment(int[] coords) {
      switch (iteration) {
        case 0:
          coords[0] = x + width;
          coords[1] = y + (height >> 1);
          return SEG_MOVETO;
        case 1:
          coords[0] = x + width;
          coords[1] = y + (int)(0.7761423749153966 * height);
          coords[2] = x + (int)(0.7761423749153966 * width);
          coords[3] = y + height;
          coords[4] = x + (width >> 1);
          coords[5] = coords[3];
          return SEG_CUBICTO;
        case 2:
          coords[0] = x + (int)(0.22385762508460333 * width);
          coords[1] = y + height;
          coords[2] = x;
          coords[3] = y + (int)(0.7761423749153966 * height);
          coords[4] = coords[2];
          coords[5] = y + (height >> 1);
          return SEG_CUBICTO;
        case 3:
          coords[0] = x;
          coords[1] = y + (int)(0.22385762508460333 * height);
          coords[2] = x + (int)(0.22385762508460333 * width);
          coords[3] = y;
          coords[4] = x + (width >> 1);
          coords[5] = coords[3];
          return SEG_CUBICTO;
        case 4:
          coords[0] = x + (int)(0.7761423749153966 * width);
          coords[1] = y;
          coords[2] = x + width;
          coords[3] = y + (int)(0.22385762508460333 * height);
          coords[4] = coords[2];
          coords[5] = y + (height >> 1);
          return SEG_CUBICTO;
        case 5:
          return SEG_CLOSE;
        default:
          throw new NoSuchElementException(
                        "Ellipse Path Iterator out of bounds");
      }
    }

  }

}
